type World = "world";

type Greeting2 = `hello ${World}`;

type EmailLocaleIDs = 'welcome_email' | 'email_heading';

type FooterLocaleIDs = 'footer_title' | 'footer_sendoff';

type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;

type Lang = 'en' | 'ja' | 'pt';

type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`;

////////////////////////////////////////////////////////////////
/// String Unions in Types
////////////////////////////////////////////////////////////////

const passedObject = {
    firstName: "Saoirse",
    lastName: "Ronan",
    age: 26,
};

interface PropEventSource<Type> {
    on(eventName: `${keyof Type & string}Changed`): void;
}

declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;

////////////////////////////////////////////////////////////////
/// Inference with Template Literals
////////////////////////////////////////////////////////////////

interface PropEventSource2<Type> {
    on<Key extends string & keyof Type>(eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void): void
}

declare function makeWatchedObject2<Type>(obj: Type): Type & PropEventSource2<Type>;

const watchedObj = makeWatchedObject2({ foo: 1 })

watchedObj.on('fooChanged', (num) => {
    num.toFixed(2);
});

export {};

////////////////////////////////////////////////////////////////
/// Intrinsic String Manipulation Types
////////////////////////////////////////////////////////////////

// Uppercase<StringType>

type Greeting = 'Hello World';
// type ShoutGreeting = "HELLO WORLD"
type ShoutGreeting = Uppercase<Greeting>;

type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`;
// type MainID = "ID-MY-APP"
type MainID = ASCIICacheKey<'my-app'>;

// Lowercase<StringType>

// type QuietGreeting = "hello world"
type QuietGreeting = Lowercase<Greeting>;

type ASCIICacheKey2<Str extends string> = `id-${Lowercase<Str>}`;
// type MainID = "id-my-app"
type MainID2 = ASCIICacheKey2<'my-app'>;

// Capitalize<StringType>

type LowercaseGreeting = 'hello world';
// type Greeting3 = "Hello world"
type Greeting3 = Capitalize<LowercaseGreeting>;

// Uncapitalize<StringType>
type UppercaseGreeting = 'HELLO WORLD';
// type UncomfortableGreeting = "hELLO WORLD"
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;
